#!/usr/bin/python
# -*- coding: utf-8 -*-
import imaplib
import smtplib
import socket

DEFAULT_MAX_MESSAGE = 50
DEFAULT_FOLDER='INBOX'

## Mailbox errors
class MailClientError(RuntimeError):
    pass

def safe_method():
    def wrap(f):
        def new_function(*args, **kw):            
            try:                
                return f(*args, **kw)            
            except Exception, e:
                raise MailClientError, e     
        return new_function
    return wrap

class MailClient(object):
    def __init__(self, host='localhost', port=None, ssl=False):
        self._mailbox = None
        self._host = host
        self._port = imaplib.IMAP4_PORT
        self._ssl = ssl
        if port:
            self._port = port
        else:
            if ssl:
                self._port = imaplib.IMAP4_SSL_PORT
    
    @safe_method()        
    def open(self, user, password):
        if(self._ssl):
            self._mailbox = imaplib.IMAP4_SSL(self._host, self._port)
        else:
            self._mailbox = imaplib.IMAP4(self._host, self._port)
        
        #TODO: check login capabilities !
        self._mailbox.login(user,password)
        
    def close(self):
        try:
            self._mailbox.close()
            self._mailbox.logout()
        except:
            pass
        finally:
            self._mailbox = None
    
    @safe_method()
    def get_message_by_id(self, message_uid):
        res, mails = self._mailbox.uid("FETCH", message_uid, '(RFC822)')
        if res in ['OK']:
            for mail in mails:
                if isinstance(mail, tuple):
                    return mail[1]
        return None
    
    @safe_method()       
    def _get_messages(self, request, max_message, header_only):
        result = []
        list_message = self._mailbox.search(None, request)[1][0].split()
        s_list_message = ",".join(list_message[0:max_message])
        if (s_list_message):
            if header_only:
                mails = self._mailbox.fetch(s_list_message, '(UID RFC822.HEADER)')[1]
                #mails = self._mailbox.fetch(s_list_message, '(UID)')[1]
            else:
                mails = self._mailbox.fetch(s_list_message, '(UID RFC822)')[1]
            for mail in mails:
                if isinstance(mail, tuple):
                    message_uid = mail[0].split()[2]
                    result.append((message_uid, mail[1]))
        return len(result), result
            
    def get_messages(self, max_message=DEFAULT_MAX_MESSAGE, header_only=False):
        return self._get_messages("(UNDELETED)", max_message, header_only)
    
    def get_unanswered_messages(self, max_message=DEFAULT_MAX_MESSAGE, header_only=False):
        return self._get_messages("(UNANSWERED)", max_message, header_only)
    
    @safe_method()
    def select(self, folder=DEFAULT_FOLDER):
        self._mailbox.select(mailbox=folder)
    
    @safe_method()
    def mark_message_as_deleted(self, message_uid):
        self._mailbox.uid("STORE", message_uid, '+FLAGS', '(\Deleted)')
    
    @safe_method()   
    def mark_message_as_answered(self, message_uid):
        self._mailbox.uid("STORE", message_uid, '+FLAGS', '(\Answered)')
    
    @safe_method()     
    def check(self) :
        self._mailbox.check()
    
    @safe_method()
    def expunge_messages(self):
        self._mailbox.expunge()
    
    @safe_method()     
    def append_message(self, folder, message):
        self._mailbox.append(folder, None, None, message)
    
    @safe_method()
    def create_folder(self, folder_name):
        ok, box_list = self._mailbox.lsub()
        if ok:
            check_box = '"%s"' % (folder_name.lower())
            test_list = [True for box in box_list if box.lower().endswith(check_box)]
            if not test_list:
                self._mailbox.create(folder_name)
                
            self._mailbox.subscribe(folder_name)
                        
    @safe_method()    
    def copy_message_to_folder(self, message_uid, folder):
        self._mailbox.uid("COPY", message_uid, folder)
        
    def __del__ (self):
        # TODO: is expunge message really needed ?
        try:
            self.expunge_messages()
        except:
            pass
        try:
            self.close()
        except:
            pass

class SMTPConnectError(StandardError):
    pass     

class SMTPClient(object):
    def __init__ (self, host, port=0, tls=False, time_out=30):
        # connection timeout 
        socket.setdefaulttimeout(time_out)
        self.host = host
        self.port = port
        self.tls = tls
        self.smtp = None
        
    def connect(self, user=None, password=None):
        try:
            self.smtp = smtplib.SMTP(self.host, self.port)
            self.smtp.ehlo()
            if self.tls:
                self.smtp.starttls()
                self.smtp.ehlo()   
            if user:
                self.smtp.login(user, password)
        except Exception, err:
            raise SMTPConnectError('Connection error: %s' % str(err))
        
    def close(self):
        try:
            self.smtp.quit()
        except:
            pass
        
    def send(self, msg, from_addr, to_addrs, mail_options=None, rcpt_options=None):
        if not mail_options:
            mail_options = []
        if not rcpt_options:
            rcpt_options = []
        try:
            self.smtp.sendmail(from_addr, to_addrs, msg, mail_options, rcpt_options)
        except Exception, err:
            raise SMTPConnectError('Send error: %s' % str(err))
            